import selectors
import socket,copy
import types,struct
import time
from threading import Timer,Thread
import public_lib
public_lib.run_path(__file__)
HOST = '0.0.0.0'    # 服务器地址
PORT = 502         # 服务器端口
ADDR = 1            # 地址
LOGGER_FILE = 'Modbus_Server_log.txt'   # 日志文件名
# 按指定长度分割列表
def chunk_list(lst, chunk_size):
    return [lst[i:i+chunk_size] for i in range(0, len(lst), chunk_size)]
# n个数据转字节串
def data2bytes(data):
    byte_data = b''
    for i in data:
        bytes_value = struct.pack('>H', i)
        byte_data += bytes_value
    return byte_data

class ModbusServer:
    '''
    '''
    def __init__(self,host='0.0.0.0',port=2404,blocks={}):
        self.selector = selectors.DefaultSelector()
        self.clients = {}               # 存储所有客户端连接 {socket: data}
        self.broadcast_timer = None # 启动定时广播
        self.stop_flag = False      # 停止标志
        self.blocks = blocks            # 块数据
        self.wdList = []                # 等待写入的数据列表
        self.setup_server(host,port)    # 启动服务端
        self.thd0 = Thread(target=self.run)
        self.thd0.daemon = True
        self.thd0.start()
    def setup_server(self,host=HOST, port=PORT):
        """初始化服务器套接字"""
        self.server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_sock.bind((host, port))
        self.server_sock.listen()
        public_lib.info(LOGGER_FILE,1,f'modbus server服务器状态',f'启动；监听地址：{host}，端口：{port}')
        self.server_sock.setblocking(False)
        self.selector.register(self.server_sock, selectors.EVENT_READ, data=None)
    def start_broadcast(self):
        """启动定时广播"""
        self.broadcast_message("服务器定时广播: 当前时间 " + time.strftime("%Y-%m-%d %H:%M:%S"))
    def broadcast_message(self, message={},mode=0):
        """向所有客户端广播消息"""
        if not self.clients or self.update_en == 0:
            return
        
    def accept_connection(self, sock):
        """接受新客户端连接"""
        conn, addr = sock.accept()
        public_lib.info(LOGGER_FILE,1,f'Modbus客户端连接',f' 地址：{addr[0]}，端口：{addr[1]}')
        conn.setblocking(False)
        data = types.SimpleNamespace(
            addr = addr,
            inb = b"",
            outb = b"",
            last_active=time.time(),# 上次连接时间
        )
        events = selectors.EVENT_READ | selectors.EVENT_WRITE
        self.selector.register(conn, events, data=data)
        self.clients[conn] = data  # 添加到客户端字典
    def remove_client(self, sock):
        """移除断开连接的客户端"""
        if sock in self.clients:
            addr = self.clients[sock].addr
            public_lib.info(LOGGER_FILE,1,f'Modbus客户端断开',f' 地址：{addr[0]}，端口：{addr[1]}')
            self.selector.unregister(sock)
            del self.clients[sock]
            sock.close()
    def service_connection(self, key, mask):
        """处理客户端连接的数据收发"""
        sock = key.fileobj
        data = key.data
        if mask & selectors.EVENT_READ:
            
            try:
                recv_data = sock.recv(1024)
                if recv_data:
                    # 更新最后活动时间
                    data.inb = recv_data
                    try:   
                        send_data = b'' 
                        length = len(recv_data)
                        data_len = len(recv_data[6:])
                        mbap_02 = struct.unpack('>H',recv_data[2:4])[0]         # 协议0=modubs，2字节
                        mbap_03 = struct.unpack('>H',recv_data[4:6])[0]         # 长度,2字节
                        unit_id = recv_data[6]                                  # modbus ID,1字节
                        modbus_code = recv_data[7]                              # modbus 功能码,1字节
                        modbus_saddr = struct.unpack('>H',recv_data[8:10])[0]   # modbus 起始地址,2字节
                        modbus_length = struct.unpack('>H',recv_data[10:12])[0] # modbus 数据长度/要写入的内容,2字节
                        # 下发数据处理
                        if length>=12 and mbap_03 == data_len and mbap_02 == 0x00:
                            # 功能码03H处理
                            if modbus_code == 0x03 :
                                if unit_id in self.blocks:
                                    block_park_data = self.blocks[unit_id][modbus_saddr:modbus_saddr+modbus_length]
                                    if len(block_park_data) == modbus_length:
                                        alength = struct.pack('<B', 3+len(block_park_data*2))
                                        m_len = struct.pack('<B', len(block_park_data*2))
                                        send_data = recv_data[:5]+alength+recv_data[6:8]+m_len+data2bytes(block_park_data)
                                    else:
                                        llegal_Data_Address = recv_data[:5]+bytes.fromhex('03018302') # 读取地址超限，返回地址超限报文
                                        send_data = llegal_Data_Address
                            # 功能码06H处理
                            elif modbus_code == 0x06:
                                try:
                                    if unit_id in self.blocks:
                                        self.blocks[unit_id][modbus_saddr] = modbus_length
                                        self.wdList.append([unit_id,modbus_saddr,modbus_length])
                                        send_data = recv_data
                                except Exception as e:
                                    send_data = recv_data[:5]+bytes.fromhex('03018602') # 读取地址超限，返回地址超限报文
                                pass
                            # 功能码10H处理
                            elif modbus_code == 0x10:
                                try:
                                    if unit_id in self.blocks:
                                        wdData = recv_data[13:]
                                        # 数据块长度 》= 要写入数据长度 + 起始地址
                                        if len(self.blocks[unit_id]) >= len(wdData)/2 + modbus_saddr:
                                            tmp_park_data = chunk_list(list(wdData),2)      # 要写入的字节串转字节列表
                                            tmp_park_data = [i[0]<<8 | i[1] for i in tmp_park_data] # 字节列表转整数列表
                                            for i in range(len(tmp_park_data)):
                                                self.blocks[unit_id][modbus_saddr+i] = tmp_park_data[i]
                                            self.wdList.append([unit_id,modbus_saddr,tmp_park_data])
                                            send_data = recv_data[:5]+bytes.fromhex('06')+recv_data[6:12]
                                        else:
                                            send_data = recv_data[:5]+bytes.fromhex('03018602') # 读取地址超限，返回地址超限报文
                                except Exception as e:
                                    # print(e)
                                    send_data = recv_data[:5]+bytes.fromhex('03018602')         # 不存在的功能码返回异常报文
                            else:
                                # 其他功能码返回异常报文
                                code_dict ={0x01:'03018101',
                                            0x02:'03018201',
                                            0x04:'03018401',
                                            0x05:'03018501',
                                            0x0f:'03018501',
                                            } 
                                if modbus_code in code_dict:
                                    send_data = recv_data[:5]+bytes.fromhex(code_dict[modbus_code]) # 不存在的功能码返回异常报文   
                        sock.send(send_data)
                    except Exception as e:
                        public_lib.info(LOGGER_FILE,0,'处理客户端数据出错',e)
                        self.remove_client(sock)
                else:
                    self.remove_client(sock)    # 客户端断开连接
                    return
            except (ConnectionError, OSError) as e:
                self.remove_client(sock)
            # print(self.wdList)
    def run(self):
        """运行服务器主循环"""
        try:
            while True:

                if self.stop_flag == True:
                    break
                events = self.selector.select(timeout=None)
                for key, mask in events:
                    if key.data is None:
                        self.accept_connection(key.fileobj)
                    else:
                        self.service_connection(key, mask)
                time.sleep(0.05)
                # 这里可以添加其他定期检查逻辑
                # 例如: 检查不活跃的客户端并断开
        except KeyboardInterrupt as e:
            pass
        finally:
            # 清理资源
            if self.broadcast_timer:
                self.broadcast_timer.cancel()
            for sock in list(self.clients.keys()):
                self.remove_client(sock)
            self.selector.unregister(self.server_sock)
            self.server_sock.close()
            self.selector.close()
            public_lib.info(LOGGER_FILE,1,f'modbus server服务器状态','已关闭')
    def stop(self):
        """停止服务器"""
        self.stop_flag = True
def server_run(host,port,data):
    server = ModbusServer(host,port,data)
    server.run()
if __name__ == "__main__":
    data = {

        1:[16712,0,2,3,4,5,6,7,8,9],
        2:[]
    }
    server = ModbusServer(HOST,502,data)
    # server.run()
    while True:
        time.sleep(1)
    pass